﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace WpfDemo.Common
{
    public static class ExpressionExtensions
    {
        private class ReplaceExpressionVisitor : ExpressionVisitor
        {
            private readonly Expression _oldValue;

            private readonly Expression _newValue;

            public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
            {
                _oldValue = oldValue;
                _newValue = newValue;
            }

            public override Expression Visit(Expression? node)
            {
                if (node == _oldValue)
                {
                    return _newValue;
                }

                return base.Visit(node);
            }
        }

        public static Expression<Func<T, bool>> CombineExpressions<T>(Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
            ReplaceExpressionVisitor replaceExpressionVisitor = new ReplaceExpressionVisitor(expression1.Parameters[0], parameterExpression);
            Expression left = replaceExpressionVisitor.Visit(expression1.Body);
            ReplaceExpressionVisitor replaceExpressionVisitor2 = new ReplaceExpressionVisitor(expression2.Parameters[0], parameterExpression);
            Expression right = replaceExpressionVisitor2.Visit(expression2.Body);
            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), new ParameterExpression[1] { parameterExpression });
        }

        public static Expression<Func<TBase, TResult>> BuildExtendSelectExpre<TBase, TResult>(this Expression<Func<TBase, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, TResult>> BuildExtendSelectExpre<TBase, T1, TResult>(this Expression<Func<TBase, T1, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, TResult>> BuildExtendSelectExpre<TBase, T1, T2, TResult>(this Expression<Func<TBase, T1, T2, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, TResult>(this Expression<Func<TBase, T1, T2, T3, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, T4, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, T4, TResult>(this Expression<Func<TBase, T1, T2, T3, T4, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, T4, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, T4, T5, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, T4, T5, TResult>(this Expression<Func<TBase, T1, T2, T3, T4, T5, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, T4, T5, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, T4, T5, T6, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, T4, T5, T6, TResult>(this Expression<Func<TBase, T1, T2, T3, T4, T5, T6, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, T4, T5, T6, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, T4, T5, T6, T7, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, T4, T5, T6, T7, TResult>(this Expression<Func<TBase, T1, T2, T3, T4, T5, T6, T7, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, T4, T5, T6, T7, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, T4, T5, T6, T7, T8, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, T4, T5, T6, T7, T8, TResult>(this Expression<Func<TBase, T1, T2, T3, T4, T5, T6, T7, T8, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, T4, T5, T6, T7, T8, TResult>>(expression);
        }

        public static Expression<Func<TBase, T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>> BuildExtendSelectExpre<TBase, T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(this Expression<Func<TBase, T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>> expression) where TResult : TBase
        {
            return GetExtendSelectExpre<TBase, TResult, Func<TBase, T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>>(expression);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "parameter");
            ParameterReplaceVisitor parameterReplaceVisitor = new ParameterReplaceVisitor(parameterExpression);
            Expression left = parameterReplaceVisitor.Visit(one.Body);
            Expression right = parameterReplaceVisitor.Visit(another.Body);
            BinaryExpression body = Expression.AndAlso(left, right);
            return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[1] { parameterExpression });
        }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "parameter");
            ParameterReplaceVisitor parameterReplaceVisitor = new ParameterReplaceVisitor(parameterExpression);
            Expression left = parameterReplaceVisitor.Visit(one.Body);
            Expression right = parameterReplaceVisitor.Visit(another.Body);
            BinaryExpression body = Expression.OrElse(left, right);
            return Expression.Lambda<Func<T, bool>>(body, new ParameterExpression[1] { parameterExpression });
        }

        public static TResult Invoke<TResult>(this Expression<Func<TResult>> expression)
        {
            return expression.Compile()();
        }

        public static TResult Invoke<T1, TResult>(this Expression<Func<T1, TResult>> expression, T1 arg1)
        {
            return expression.Compile()(arg1);
        }

        public static TResult Invoke<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, T1 arg1, T2 arg2)
        {
            return expression.Compile()(arg1, arg2);
        }

        public static TResult Invoke<T1, T2, T3, TResult>(this Expression<Func<T1, T2, T3, TResult>> expression, T1 arg1, T2 arg2, T3 arg3)
        {
            return expression.Compile()(arg1, arg2, arg3);
        }

        public static TResult Invoke<T1, T2, T3, T4, TResult>(this Expression<Func<T1, T2, T3, T4, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4);
        }

        public static TResult Invoke<T1, T2, T3, T4, T5, TResult>(this Expression<Func<T1, T2, T3, T4, T5, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4, arg5);
        }

        public static TResult Invoke<T1, T2, T3, T4, T5, T6, TResult>(this Expression<Func<T1, T2, T3, T4, T5, T6, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4, arg5, arg6);
        }

        public static TResult Invoke<T1, T2, T3, T4, T5, T6, T7, TResult>(this Expression<Func<T1, T2, T3, T4, T5, T6, T7, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
        }

        public static TResult Invoke<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(this Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
        }

        public static TResult Invoke<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(this Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
        }

        public static TResult Invoke<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(this Expression<Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>> expression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10)
        {
            return expression.Compile()(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
        }

        public static object GetConstantValue(this Expression expression)
        {
            GetConstantValueVisitor getConstantValueVisitor = new GetConstantValueVisitor();
            getConstantValueVisitor.Visit(expression);
            return getConstantValueVisitor.ConstantValue;
        }

        public static object GetMemberValue(this Expression expression)
        {
            GetMemberValueVisitor getMemberValueVisitor = new GetMemberValueVisitor();
            getMemberValueVisitor.Visit(expression);
            return getMemberValueVisitor.Value;
        }

        private static Expression<TDelegate> GetExtendSelectExpre<TBase, TResult, TDelegate>(Expression<TDelegate> expression)
        {
            NewExpression newExpression = Expression.New(typeof(TResult));
            MemberInitExpression memberInitExpression = (MemberInitExpression)expression.Body;
            ParameterExpression[] array = expression.Parameters.ToArray();
            List<string> existsProperties = new List<string>();
            foreach (MemberBinding binding in memberInitExpression.Bindings)
            {
                existsProperties.Add(binding.Member.Name);
            }

            List<MemberBinding> list = new List<MemberBinding>();
            IEnumerable<PropertyInfo> enumerable = from x in typeof(TResult).GetProperties()
                                                   where !existsProperties.Contains(x.Name)
                                                   select x;
            foreach (PropertyInfo aProperty in enumerable)
            {
                if (typeof(TBase).GetMembers().Any((MemberInfo x) => x.Name == aProperty.Name))
                {
                    MemberInfo member = typeof(TBase).GetMember(aProperty.Name)[0];
                    MemberBinding item = Expression.Bind(member, Expression.Property(array[0], aProperty.Name));
                    list.Add(item);
                }
            }

            list.AddRange(memberInitExpression.Bindings);
            MemberInitExpression body = Expression.MemberInit(newExpression, list.ToArray());
            return Expression.Lambda<TDelegate>(body, array);
        }
    }

    public class ParameterReplaceVisitor : ExpressionVisitor
    {
        private ParameterExpression _parameter { get; set; }

        public ParameterReplaceVisitor(ParameterExpression paramExpr)
        {
            _parameter = paramExpr;
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            if (p.Type == _parameter.Type)
            {
                return _parameter;
            }

            return p;
        }
    }

    public class GetConstantValueVisitor : ExpressionVisitor
    {
        public object ConstantValue { get; set; }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            ConstantValue = node.Value;
            return base.VisitConstant(node);
        }
    }

    public class GetMemberValueVisitor : ExpressionVisitor
    {
        public object Value { get; set; }
    }

}
